package toolx

import (
	"context"
	"fmt"
	"regexp"
	"strconv"
	"strings"

	"gitee.com/chunanyong/zorm"
	"gitee.com/workits/pkgs/dbx"
)

type SQLiteTableInfoEntity struct {
	zorm.EntityStruct

	Name      string `column:"name"`
	Type      string `column:"type"`
	Notnull   int    `column:"notnull"`
	DfltValue string `column:"dflt_value"`
	PK        int    `column:"pk"`
}

type SQLiteTool struct {
	dbCfg dbx.Config
}

func (t *SQLiteTool) TableToStructInfo(dbName, tableName, ignorePrefix, structName string, isStandard bool) StructInfo {
	tb := t.getTableInfo(tableName)
	tbName := tb.Name
	if len(structName) == 0 {
		if len(ignorePrefix) > 0 {
			tbName = tbName[len(ignorePrefix):]
		}
	} else {
		tbName = structName
	}
	tbName = strings.TrimLeft(tbName, "_")

	structInfo := StructInfo{
		Name:       UderscoreToLowerCamelCase(tbName),
		StructName: UderscoreToUpperCamelCase(tbName),
		TableName:  tb.Name,
		FileName:   CamelCaseToUdnderscore(tbName),
		Comment:    tb.Comment,
		IsStandard: isStandard,
	}

	var fields []StructField
	for _, col := range tb.Fields {
		field := StructField{
			FieldName:  UderscoreToUpperCamelCase(col.Field),
			ColumnName: col.Field,
			JsonName:   UderscoreToLowerCamelCase(col.Field),
			Comment:    col.Comment,
		}

		// 是否必填项
		if col.Null == "NO" {
			field.Required = true
		}

		// 默认值
		if isStandard {
			if field.FieldName == "CreatedAt" || field.FieldName == "UpdatedAt" {
				field.Default = "time.Now()"
			}
		} else {
			if len(col.Default) > 0 {
				switch col.Default {
				case "datetime('now', 'localtime')", "datetime('now')", "CURRENT_TIME", "CURRENT_TIMESTAMP":
					field.Default = "time.Now()"
				default:
					field.Default = "\"" + col.Default + "\""
				}
			}
		}

		// 数据类型转换：db -> golang
		typ := strings.ToUpper(col.Type)
		if strings.Index(typ, "TIME") == 0 || strings.Index(typ, "DATE") == 0 {
			field.Type = "time.Time"
			if structInfo.IsStandard {
				if field.FieldName != "CreatedAt" && field.FieldName != "UpdatedAt" && field.FieldName != "DeletedAt" {
					structInfo.HasTime = true
				}
			} else {
				structInfo.HasTime = true
			}
		} else {
			switch typ {
			case "INTEGER", "INTEGER UNSIGNED":
				if structInfo.IsStandard && field.FieldName == "Id" {
					field.Type = "int64"
				} else {
					field.Type = "int"
				}
			case "TINYINT":
				field.Type = "int8"
			case "SMALLINT", "SMALLINT UNSIGNED":
				field.Type = "int16"
			case "BIGINT", "BIGINT UNSIGNED":
				field.Type = "int64"
			case "REAL":
				field.Type = "float64"
			case "DECIMAL":
				field.Type = "decimal.Decimal"
				structInfo.HasDecimal = true
			case "TINYINT UNSIGNED":
				field.Type = "byte"
			case "BLOB":
				field.Type = "[]byte"
			case "BOOL":
				field.Type = "bool"
			default:
				field.Type = "string"
			}
		}

		// 主健
		if col.Key == "PRI" {
			structInfo.PrimaryKeyColumn = field.ColumnName
			structInfo.PrimaryKeyField = field.FieldName
			structInfo.PrimaryKeyName = field.JsonName
			structInfo.PrimaryKeyType = field.Type
			field.IsPrimaryKey = true
		}

		fields = append(fields, field)
	}
	structInfo.Fields = fields

	return structInfo
}

func (t *SQLiteTool) getTableInfo(tableName string) *TableInfoEntity {
	if dbx.GetDefaultDao() == nil {
		if err := dbx.InitConn(t.dbCfg); err != nil {
			panic("🙅数据库初始化失败")
		}
		zorm.FuncPrintSQL = func(ctx context.Context, sqlstr string, args []interface{}, execSQLMillis int64) {}
	}

	ctx := context.Background()

	// 获取表注释
	finder := zorm.NewSelectFinder("sqlite_master", "name")
	finder.Append("where type = ?", "table")
	finder.Append("and name = ?", tableName)
	tb := &TableInfoEntity{}
	if has, err := zorm.QueryRow(ctx, finder, tb); err != nil {
		panic(err)
	} else {
		if !has {
			panic("🙅数据库表不存在")
		}
	}

	// 获取字段信息
	finder = zorm.NewFinder()
	finder.Append(fmt.Sprintf("pragma table_info(%s)", tableName))
	finder.InjectionCheck = false
	finder.SelectTotalCount = false
	var cols []SQLiteTableInfoEntity
	if err := zorm.Query(ctx, finder, &cols, nil); err != nil {
		panic(err)
	}
	fields := make([]*ColumnInfoEntity, 0)
	reg := regexp.MustCompile(` \((.*?)\)`)
	for _, col := range cols {
		field := &ColumnInfoEntity{
			Field:   col.Name,
			Default: col.DfltValue,
		}
		if strings.Contains(col.Type, "(") {
			field.Type = reg.ReplaceAllString(col.Type, "")
			matchRets := reg.FindStringSubmatch(col.Type)
			if len(matchRets) > 1 {
				field.Len, _ = strconv.Atoi(matchRets[1])
			}
		} else {
			field.Type = col.Type
		}
		if col.Notnull == 1 {
			field.Null = "NO"
		} else {
			field.Null = "YES"
		}
		if col.PK == 1 {
			field.Key = "PRI"
		}
		fields = append(fields, field)
	}
	tb.Fields = fields

	return tb
}
